package negocio;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import exceptions.CajaException;
import exceptions.MesaException;
import beans.CajaVO;
import beans.CierreCajaVO;
import beans.ComisionMozoVO;
import beans.FacturaVO;
import beans.MozoVO;


@Entity
@Table(name="cajas")
public class Caja implements Serializable
{
	private static final long serialVersionUID = 1L;
	
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	@Column(name="id_caja", nullable=false)
	private int id_caja;
	
	@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
	@JoinColumn(name="id_caja", referencedColumnName="id_caja")
	private Set<Factura> facturas= new HashSet<Factura>();
	
	@OneToOne(cascade=CascadeType.ALL)
	@JoinColumn(name="id_salon", referencedColumnName="id_salon")
	private Salon salon;
	
	@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
	@JoinColumn(name="id_caja", referencedColumnName="id_caja")
	private List<Mozo> mozos;
	
	@Column(name="abierta")
	private boolean isAbierta;
	
	@OneToMany(cascade=CascadeType.ALL)
	@JoinColumn(name="id_caja", referencedColumnName="id_caja")
	private List<ComisionMozo>comisionMozos= new ArrayList<ComisionMozo>();
	
	@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
	@JoinColumn(name="id_caja", referencedColumnName="id_caja")
	private Set<CierreCaja> cierreCaja= new HashSet<CierreCaja>();
	
	
	public Caja()
	{
		
	}
	
	public Set<Factura> getFacturas() {
		return facturas;
	}
	public void setFacturas(HashSet<Factura> facturas) {
		this.facturas = facturas;
	}
	public Salon getSalon() {
		return salon;
	}
	public void setSalon(Salon salon) {
		this.salon = salon;
	}
	public List<Mozo> getMozos() {
		return mozos;
	}
	public void setMozos(List<Mozo> mozos) {
		this.mozos = mozos;
	}

	public boolean isAbierta() {
		return isAbierta;
	}

	public void setAbierta(boolean isAbierta) {
		this.isAbierta = isAbierta;
	}

	public int getId_caja() {
		return id_caja;
	}


	public void setId_caja(int id_caja) {
		this.id_caja = id_caja;
	}


	public Set<CierreCaja> getCierreCaja() {
		return cierreCaja;
	}


	public void setCierreCaja(HashSet<CierreCaja> cierreCaja) {
		this.cierreCaja = cierreCaja;
	}
	
	
	public List<ComisionMozo> getComisionMozos() {
		return comisionMozos;
	}

	public void setComisionMozos(List<ComisionMozo> comisionMozos) {
		this.comisionMozos = comisionMozos;
	}
	
	public CajaVO getVO() 
	{
		CajaVO cajaVO = new CajaVO();
		cajaVO.setAbierta(isAbierta());
		HashSet<FacturaVO> factsVO = new HashSet<FacturaVO>();
		Iterator<Factura> facturas = getFacturas().iterator();
		while(facturas.hasNext())
		{
			factsVO.add(((Factura) facturas.next()).getVO());
		}
		cajaVO.setFacturas(factsVO);

		HashSet<CierreCajaVO> cierreJacaVO = new HashSet<CierreCajaVO>();
		Iterator<CierreCaja> it = getCierreCaja().iterator();
		while(it.hasNext())
		{
			CierreCaja c = (CierreCaja) it.next();
			cierreJacaVO.add(c.getVO());
		}
		cajaVO.setCierreCaja(cierreJacaVO);
		
		ArrayList<ComisionMozoVO> comicionMozosVO = new ArrayList<ComisionMozoVO>();
		for (int i = 0 ; i < getComisionMozos().size() ; i++)
		{
			comicionMozosVO.add(getComisionMozos().get(i).getVO());
		}
		cajaVO.setComisionMozos(comicionMozosVO);

		cajaVO.setId_caja(getId_caja());

		ArrayList<MozoVO> mozosVO = new ArrayList<MozoVO>();
		for (int i = 0 ; i < getMozos().size() ; i++)
		{
			mozosVO.add(getMozos().get(i).getVO());
		}
		cajaVO.setMozos(mozosVO);

		cajaVO.setSalon(getSalon());
		return cajaVO;
		
	}	
	@Override
	public String toString() {
		return "Caja [id_caja=" + id_caja + ", facturas=" + facturas
				+ ", salon=" + salon + ", mozos=" + mozos + ", estado="
				+ isAbierta + ", comisionMozos=" + comisionMozos + ", cierreCaja="
				+ cierreCaja + "]";
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((cierreCaja == null) ? 0 : cierreCaja.hashCode());
		result = prime * result
				+ ((comisionMozos == null) ? 0 : comisionMozos.hashCode());
		result = prime * result
				+ ((facturas == null) ? 0 : facturas.hashCode());
		result = prime * result + id_caja;
		result = prime * result + (isAbierta ? 1231 : 1237);
		result = prime * result + ((mozos == null) ? 0 : mozos.hashCode());
		result = prime * result + ((salon == null) ? 0 : salon.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Caja other = (Caja) obj;
		if (cierreCaja == null) {
			if (other.cierreCaja != null)
				return false;
		} else if (!cierreCaja.equals(other.cierreCaja))
			return false;
		if (comisionMozos == null) {
			if (other.comisionMozos != null)
				return false;
		} else if (!comisionMozos.equals(other.comisionMozos))
			return false;
		if (facturas == null) {
			if (other.facturas != null)
				return false;
		} else if (!facturas.equals(other.facturas))
			return false;
		if (id_caja != other.id_caja)
			return false;
		if (isAbierta != other.isAbierta)
			return false;
		if (mozos == null) {
			if (other.mozos != null)
				return false;
		} else if (!mozos.equals(other.mozos))
			return false;
		if (salon == null) {
			if (other.salon != null)
				return false;
		} else if (!salon.equals(other.salon))
			return false;
		return true;
	}
	
	//CALCULAR COMISIONES DE MOZO
	public List<ComisionMozo> calcularComisionMozo (Date date){
		ComisionMozo cm = new ComisionMozo();
		ComisionMozoID id = new ComisionMozoID();
		for(Mozo mozo :this.mozos){
		
		id.setFecha(date);
		id.setMozo(mozo);
		
		Float monto=(float) 0;
		List<Factura>facturas= this.getFacturasxMozo(mozo);
		facturas= this.getFacturasxFecha(facturas, date);
		
		for(Factura f: facturas)
			monto=f.getTotal() + monto;
		
		monto=(monto * mozo.getPorcentaje_Comision())/100;
		float extra= this.comisionExtra(facturas);
		monto = monto+extra;
		
		cm.setComision(monto);
		cm.setMozo(mozo);
		cm.setId(id);
		this.comisionMozos.add(cm);
		}
		
		return this.comisionMozos;
	}

	private List<Factura> getFacturasxFecha(List<Factura> facturas, Date date){
		List<Factura>lista= new ArrayList<>();
		for(Factura f : facturas)
			
			if(f.getFecha_de_emision().equals(date))
				lista.add(f);
		return lista;
	}
	
	private List<Factura> getFacturasxMozo(Mozo mozo){
		int idmesa;
		int i;
		List<Factura>facturas= new ArrayList<>();
		for(Factura f : this.getFacturas()){
			idmesa=f.getMesa().getId_numeroMesas();
			for( i=0;i<mozo.getSector().getMesas().size() 
					&& idmesa!=mozo.getSector().getMesas().get(i).getId_numeroMesas();i++)
				;
			if(i<mozo.getSector().getMesas().size())
				facturas.add(f);
			}
		
		return facturas;
	}

	private float comisionExtra(List<Factura> facturas) {
		float extra=0;
		for(Factura f :facturas ){
			for(Pedido p : f.getMesa().getPedidos()){
				for(Plato plato : p.getPlatos()){
					extra=(plato.getPrecio() * plato.getComision_extra())/100 + extra;
				}
			}
		}
		return extra;
	}
	
	public CierreCaja cerrarCaja(float montoAmano) throws CajaException{
		if(isAbierta){
			CierreCaja cierre = new CierreCaja();			
			float debito=0, credito=0,efectivo =0;
			
			for(Factura f : facturas){
				if(f.getFecha_de_emision().equals(new Date())){
					if(f.getFormaPago().equalsIgnoreCase("efectivo"))
						efectivo=efectivo+f.getTotal();
					if(f.getFormaPago().equalsIgnoreCase("debito"))
						debito=debito+f.getTotal();
					if(f.getFormaPago().equalsIgnoreCase("credito"))
						credito=credito+f.getTotal();
				}
			}
			cierre.setFecha(new Date());
			cierre.setMontoAmano(montoAmano);
			cierre.setMontoContadoFacturas(efectivo);
			cierre.setMontoCreditoFacturas(credito);
			cierre.setMontoDebitoFacturas(debito);
			
			isAbierta = false;
			cierreCaja.add(cierre);
			
			return cierre;
		}
		else
			throw new CajaException("La caja ya se encuentra cerrada.");
	}
	
	public void abrirCaja() throws CajaException
	{
		if(canAbrirCaja()){
			isAbierta = true;
		}
		else
			throw new CajaException("La caja se encuentra cerrada, no puede volver a abrirse hoy.");
	}
	
	private boolean canAbrirCaja()
	{
		Date currentDate = getDateWOHours(new Date());
		for(CierreCaja cierre : cierreCaja){
			System.out.println(cierre.getFecha());
			if(getDateWOHours(cierre.getFecha()).equals(currentDate))
				return false;
		}
		return true;
	}
	
	@SuppressWarnings("deprecation")
	private Date getDateWOHours(Date date)
	{
		Date currentDate = new Date(date.getYear(),date.getMonth(),date.getDate());
		return currentDate;
	}

	public void agregarMozo(Mozo m) {
		mozos.add(m);
	}
	
	public Factura generarFactura(int numeroMesa, String formaPago) 
			throws MesaException
	{
		Mesa mesa = salon.getMesa(numeroMesa);
		Factura factura = new Factura();
		factura.setMesa(mesa);
		factura.setFecha_de_emision(new Date());
		factura.setFormaPago(formaPago);
		factura.calcularTotal();
		
		facturas.add(factura);
		return factura;
	}
}
